home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d18 / kruse_11.arc / 11TEXT.EXT < prev    next >
Text File  |  1990-11-30  |  27KB  |  761 lines

  1.  
  2. {11.2  Structuring the Data:  The Main Program}
  3.  
  4.  
  5. Program IndexText(InText, InIndex, NewIndex, OutIndex, HashFile,
  6.                   NewHashFile, Input, Output);
  7. {Produces word counts and list of references for the document file InText.
  8.  Uses the master word list in file InIndex, if provided.
  9.  Output word list for the new text goes to file NewIndex.
  10.  The merger of these two files becomes OutIndex.
  11.  HashFile  contains the common words to be ignored.  If not specified, it is
  12.  created on output, containing the words so flagged by the user.}
  13. Const
  14.   maxwd         =   20;    {More letters in a word will be ignored.}
  15.   minwd         =    3;    {Shorter words will be ignored}
  16.   hashsize      = 2003;    {should be a prime;  size of hash table}
  17.   linesperpage  =   66;    {assumes standard spacing and paper}
  18.   maxheight     =   20;    {for building binary tree in phase 2}
  19.   A = 'A';
  20.   Z = 'Z';
  21.   hyphen        =  '-';
  22.   blank         =  ' ';
  23.   apostrophe    = '''';    {requires two apostrophes to represent one}
  24.   underscore    =  '_';
  25.   ordbackspace  =    8;    {ASCII control character for backspace}
  26.   ordformfeed   =   12;    {ASCII control character for new page}
  27.   changecase    =   32;    {ASCII difference between upper and lower case}
  28. Type
  29.   word          =  packed array[1..maxwd] of char;
  30.   reference     =  record
  31.                      wd:   word;
  32.                      pg:   integer;  {page number}
  33.                  end;
  34.   fileref       =  file of reference;  {used for local files}
  35.   letter        =  A..Z;
  36.   hashentry     =  1..hashsize;
  37. Var
  38.   InText,                       {document being processed}
  39.   InIndex,                      {master word list}
  40.   NewIndex,                     {word list of current document}
  41.   OutIndex:     text;           {updated master word list}
  42.   HashFile, 
  43.   NewHashFile:  file of word;   {local file, used to update HashFile}
  44.   RefFile:      array[letter] of fileref;     {local files used for
  45.                      auxiliary storage of words from phase 1 to phase 2:
  46.                      separate file for each initial letter}
  47.   blankword:    word;           {will contain all blanks}
  48.   outcount:    array[letter] of integer;  {counters for word files}
  49.   wordcount:   integer;         {count of all words in the text}
  50.  
  51. Begin                           {main program}
  52.   SplitWords;                   {phase 1}
  53.   ClassifyWords;                {phase 2}
  54.   UpdateHashFile;               {phase 3, first part}
  55.   MergeIndices;                 {phase 3, second part}
  56. End.
  57.  
  58.  
  59.  
  60.  
  61. Function Lt(u,v:  word):   Boolean;
  62. {Determine if word u precedes word v lexicographically.}
  63. Var
  64.   i:    1..maxwd;               {loop variable}
  65. Begin                           {function Lt}
  66.   i := 1;
  67.   While (i < maxwd) and (u[i] = v[i]) do   i := i + 1;
  68.   Lt := (u[i] < v[i])
  69.   {Above is version that works with ASCII code. For codes where blank comes 
  70.                 after letters, modifications are necessary.}
  71. End;                            {function Lt}
  72.  
  73.  
  74. Procedure ReadWord( var F: text;  var w: word);
  75. {reads word w from text file F; assumes not at end of file}
  76. Var
  77.   c:  1..maxwd;
  78. Begin                           {procedure ReadWord}
  79.   For c := 1 to maxwd do
  80.     read(F, w[c])
  81. End;                            {procedure ReadWord}
  82.  
  83.  
  84.  
  85.  
  86. procedure WriteWord(var F: text; w: word);
  87. {writes word w to text file F}
  88. var
  89.   c:  1..maxwd;
  90. begin
  91.   for c := 1 to maxwd do
  92.     write(F, w[c])
  93. end;
  94.  
  95.  
  96.  
  97.  
  98. {11.3  Phase 1:  Splitting the Text into Words}
  99.  
  100.  
  101. Procedure SplitWords;
  102. {sets up hash table, reads text, and divides into 26 word lists}
  103. Var
  104.   hash:        array[hashentry] of word;     {hash table}
  105.   pagecount,                    {keeps the current page number}
  106.   addpage,                      {amount to increase pagecount after word}
  107.   linecount:   integer;         {line number on the current page}
  108.   w:           word;            {word currently being processed}
  109.   x:           hashentry;       {location of w, if in hash table}
  110.   endinput:    Boolean;         {true if and only if input has all been read}
  111.   firstletter: char;            {Into which file does word w go?}
  112. {The following are kept for use in procedure GetWord,
  113.  and for efficiency are set up only once in procedure Initialize.}
  114.   backspace,
  115.   formfeed:    char;            {ASCII control characters}
  116.   contchar,                     {characters OK in the middle of a word}
  117.   alphabet:    set of char;     {letters only --- to start a word}
  118. {Implementation dependent: A good Pascal compiler should allow "set of char";
  119.  otherwise, a restricted range is required.}
  120.  
  121. Begin                           {procedure SplitWords}
  122.   Initialize;                   {sets up files, hash table, constants}
  123.   GetWord(w);                   {obtains a single word from InText}
  124.   While not endinput do
  125.   Begin
  126.     x := HashAddress(w);
  127.     If w <> hash[x] then
  128.     Begin                       {Not in hash table; put into RefFile.}
  129.       firstletter := w[1];
  130.       outcount[firstletter] := outcount[firstletter] + 1;
  131.       With RefFile[firstletter]^ do
  132.       Begin
  133.         wd := w;
  134.         pg := pagecount
  135.       End;
  136.       Put(RefFile[firstletter])
  137.     End;
  138.     GetWord(w)
  139.   End;
  140.   Conclude                      {writes word counts to Output}
  141. End;                            {procedure SplitWords}
  142.  
  143.  
  144.  
  145. Function HashAddress(w: word): hashentry;
  146. {calculates the location in hash table of word w, or, if none,
  147.  returns pointing to the blank word where w should go}
  148. Var
  149.   x,                         {calculated location}
  150.   inc: integer;          {increment for open addressing}
  151. Begin                        {function HashAddress}
  152.   x := (ord(w[1]) * ord(w[3]) * ord(w[4]) + ord(w[6])) mod hashsize + 1;
  153.   {Hash function assumes long word length. For short word machines, we
  154.    must ensure that the result is nonnegative, and worry about overflow.}
  155.   If (hash[x] <> w) and (hash[x] <> blankword) then
  156.   Begin
  157.     inc   := 1;
  158.     Repeat
  159.       x := x + inc;
  160.       If x > hashsize then x := x - hashsize;
  161.       inc := inc + 2
  162.     Until (w = hash[x]) or (blankword = hash[x])
  163.   End;
  164.   HashAddress := x
  165. End;                          {function HashAddress}
  166.  
  167.  
  168.  
  169. Procedure Initialize;
  170. {sets up constant-valued sets for use in GetWord;
  171.  opens the text file  and initializes various counters;
  172.  opens file holding hash table (if any), and reads or
  173.  otherwise initializes the table.}
  174. Var
  175.   ch:         char;             {used as an index}
  176.   i:          integer;          {general--purpose loop control}
  177. Begin                           {procedure Initialize}
  178.   backspace:= chr(ordbackspace);
  179.   formfeed := chr(ordformfeed); {Initialize ASCII control characters.}
  180.   alphabet := ['A'..'Z', 'a'..'z'];      {letters only, to start a word}
  181.   contchar := alphabet + [hyphen, apostrophe, backspace, underscore];
  182.                         {characters that will not terminate the word}
  183.   For i := 1 to maxwd do
  184.     blankword[i] := blank;
  185.   reset(InText);
  186.   endinput := eof(InText);
  187.   Repeat
  188.     write( 'What is the page number on which the text begins?');
  189.     readln(pagecount);
  190.     if pagecount < 0 then
  191.       writeln('Must be a nonnegative integer.')
  192.   until pagecount >= 0;
  193.   linecount := 0;
  194.   addpage   := 0;
  195.   wordcount := 0;
  196.   For ch := A to Z do
  197.   Begin
  198.     Rewrite( RefFile[ch] );
  199.     Outcount[ch] := 0
  200.   End;
  201.   reset(HashFile);
  202.   if eof(HashFile) then
  203.   begin     {There is no previous table; initialize the table to all blanks.}
  204.     writeln('Cannot open file for hash table. Creating a new table.');
  205.     for i := 1 to hashsize do
  206.       hash[i] := blankword
  207.   end 
  208.   else begin                {Retrieve the previous hash table.}
  209.     i := 0;
  210.     repeat
  211.       i := i + 1;
  212.       hash[i] := HashFile^;
  213.       get(HashFile)
  214.     until eof(HashFile) or (i >= hashsize);
  215.     if (not eof(HashFile)) or (i <> hashsize) then
  216.       writeln('Error in reading hash table. Incorrect number of entries.')
  217.   end
  218. end;                            {procedure Initialize}
  219.  
  220.  
  221.  
  222. Procedure GetWord( var w: word);
  223. {Gets words from input file InText, and returns only words
  224.  at least minwd characters long.  Parameter endinput becomes
  225.  true if and only if the end of InText is reached with no word to return.
  226.  This parameter is set by the subsidiary procedure GetChar.
  227.  The procedure also updates global variables wordcount and linecount,
  228.  updates the global variable pagecount after each linesperpage cr's,
  229.  or after each formfeed, whichever comes first, and
  230.  uses the sets alphabet and contchar and various character constants.}
  231. label 1;           {used by GetChar to exit procedure on eof(InText)}
  232. Var  c:      0..maxwd;          {count of characters in word}
  233.      ch:     char;              {character currently processed}
  234.      endln:  Boolean;           {At the end of a line?}
  235. begin                           {procedure GetWord}
  236.   repeat                   {until current word is at least minwd chars long}
  237.     c := 0;
  238.     repeat
  239.       GetChar(ch)               {Find a letter that will start the word.}
  240.     until ch in alphabet;
  241.     pagecount := pagecount + addpage;
  242.     addpage := 0;
  243.     If ch in ['a'..'z'] then    {Translate the first letter to uppercase.}
  244.       ch := chr(ord(ch) - changecase);      {system dependent}
  245.     AddChar(ch);                {Put first letter into the word.}
  246.     GetChar(ch);
  247.     While ch in contchar do
  248.       If ch in alphabet then    {Add letters directly to word.}
  249.       Begin                     {processing letter}
  250.         AddChar(ch);
  251.         GetChar(ch)
  252.       End                       {processing letter}
  253.       Else If ch = hyphen then
  254.       Begin                     {processing hyphen}
  255.         GetChar(ch);            {Find what comes after hyphen.}
  256.         If endln then
  257.           GetChar(ch)           {Delete both the hyphen and the end of line.}
  258.         Else if ch = hyphen then    {Two hyphens represent a dash.}
  259.           ch := blank           {Use a blank to terminate the word.}
  260.         Else If ch in alphabet then
  261.           AddChar(hyphen)       {Include hyphens between letters}
  262.         Else    {nothing}       {Delete all other hyphens}
  263.       End                       {processing hyphen}
  264.       Else if ch = apostrophe then
  265.       Begin                     {processing apostrophe}
  266.         GetChar(ch);
  267.         If ch = 's' then        {Delete 's at end of word only.}
  268.         Begin
  269.           GetChar(ch);
  270.           If ch in contchar then
  271.           Begin
  272.             AddChar(apostrophe);
  273.             AddChar('s')
  274.           End
  275.         End
  276.         Else if ch in alphabet then
  277.            AddChar(apostrophe)  {Allow contractions.}
  278.       End                       {processing apostrophe}
  279.       Else         {Remaining possibilities are backspace and underscore.}
  280.         GetChar(ch);            {Delete these characters.}
  281.               {while loop on continuing characters ends here.}
  282.     wordcount := wordcount + 1
  283.   Until c >= minwd;             {Skip over short words.}
  284.   While c < maxwd do            {Fill with blanks.}
  285.   Begin
  286.     c := c + 1;
  287.     w[c] := blank
  288.   End;
  289. 1:                 {When end of file occurs, program will exit to here from GetChar.}
  290. End;                            {procedure GetWord}
  291.  
  292.  
  293.  
  294. Procedure GetChar(var ch: char);
  295. {gets a character from input text into ch; checks for eof;
  296.  updates page count and line count}
  297. Begin                           {procedure GetChar}
  298.   If eof(InText) then
  299.     If c >= minwd then
  300.       ch := '.'                 {special character to end the current word}
  301.     Else begin                  {no word to return; set endinput}
  302.       endinput := true;
  303.       goto 1                    {Exit from GetWord.}
  304.     End
  305.   Else begin                    {not at end of file: process next character}
  306.     ch := InText^;
  307.     endln := eoln(InText);
  308.     get(InText);
  309.     If endln then
  310.     Begin
  311.       linecount := linecount + 1;
  312.       If linecount >= linesperpage then
  313.         Begin
  314.           addpage := addpage + 1;
  315.           linecount := 0
  316.         End
  317.     End;
  318.     If ch = formfeed then
  319.     Begin
  320.       addpage := addpage + 1;
  321.       linecount := 0;
  322.       endln := true;        {Treat formfeed like end of line.}
  323.       ch := blank
  324.     End
  325.   End
  326. End;        {procedure GetChar}
  327.  
  328.  
  329.  
  330. Procedure AddChar(ch: char);
  331. {adds given character to word, if possible}
  332. Begin                           {procedure AddChar}
  333.   If c < maxwd then
  334.   Begin
  335.     c := c + 1;
  336.     w[c] := ch
  337.   End
  338. End;                            {procedure AddChar}
  339.  
  340.  
  341.  
  342. Procedure Conclude;
  343. {Writes out counts of various word lists. For some systems, it is 
  344.    necessary to close files, which should be done in this procedure.}
  345. Var
  346.   ch:      char;                {loop index}
  347. Begin                           {procedure Conclude}
  348.   writeln('The total number of words read in is ', wordcount:7);
  349.   writeln;
  350.   writeln('The number of words to process further in the next stage,');
  351.   writeln('beginning with each letter, is below.');
  352.   writeln;
  353.   for ch := 'A' to 'M' do write(' ', ch:1, ' ');
  354.   writeln;
  355.   for ch := 'A' to 'M' do write(outcount[ch]:4, ' ');
  356.   writeln;
  357.   writeln;
  358.   for ch := 'N' to 'Z' do write(' ', ch:1, ' ');
  359.   writeln;
  360.   for ch := 'N' to 'Z' do write(outcount[ch]:4, ' ');
  361.   writeln;
  362.   writeln
  363. End;                            {procedure Conclude}
  364.  
  365.  
  366.  
  367.  
  368.  
  369. {11.4  Phase 2:  Classifying the words}
  370.  
  371.  
  372. Procedure ClassifyWords;
  373. {For each letter of the alphabet, the procedure reads in a list of words from
  374.  InIndex, builds them into a binary tree, supplements it with entries from
  375.  RefFile, and writes result to NewIndex and NewHashFile.}
  376. Type
  377.   wordtype  = (hash, count, index);     {three ways to process a word}
  378.   pointref  = ^reflist;
  379.   reflist   = record                    {list of references}
  380.                 pg:   integer;
  381.                 next: pointref
  382.               end;
  383.   pointer   = ^node;
  384.   node      = record                    {vertex of the binary tree}
  385.                 wd:       word;
  386.                 left,
  387.                 right:    pointer;
  388.               case kind:  wordtype of
  389.                 hash:
  390.                   ();     {empty}
  391.                 count:
  392.                   (ct:    integer);
  393.                 index:
  394.                   (ref:   pointref)
  395.               end;
  396. Var
  397.   root:       pointer;          {root of the binary tree}
  398.   ch:         char;             {Loop on the first letter of word.}
  399. Begin                           {procedure ClassifyWords}
  400.   writeln('At the appearance of each word, give its disposition:');
  401.   writeln(' F --- Forget all occurrences of this word.');
  402.   writeln(' C --- Count how many times this word appears.');
  403.   writeln(' I --- Index this word: list the pages on which it appears.');
  404.   Reset(InIndex);
  405.   Rewrite(NewIndex);
  406.   For ch := A to Z do           {Start main loop on first letter of word.}
  407.   Begin
  408.     BuildTree(root, ch);  {Get the part of master wordlist starting with ch
  409.                     from the file InIndex, and build it into a binary tree.}
  410.     reset(RefFile[ch]);
  411.     While not eof(RefFile[ch]) do
  412.     Begin
  413.       Process(RefFile[ch]^); 
  414.           {Use new words from RefFile[ch] to update the tree.}
  415.       get( RefFile[ch] )
  416.     End;
  417.     OutputTree(root)   {Write the contents of the tree into files NewIndex and 
  418.                                 NewHashFile.}
  419.   End                           {main loop on letters of alphabet}
  420. End;                            {procedure ClassifyWords}
  421.  
  422.  
  423.  
  424. Procedure GetNode( var p: pointer;  ch: char);
  425. {reads a word from file  InIndex  and sets node correspondingly;
  426. returns p = nil at eof or when next word starts later than ch}
  427. Var
  428.   wordcode:  char;              {letter indicating type of word}
  429. Begin                           {procedure GetNode}
  430.   While (not eof(InIndex)) and (InIndex^ = blank) do
  431.     Get(InIndex);               {Skip all the leading blanks.}
  432.   If eof(InIndex) then
  433.     p := nil
  434.   Else if InIndex^ > ch then
  435.     p := nil
  436.   Else begin
  437.     new(p);
  438.     with p^ do begin
  439.       ReadWord(InIndex, wd);
  440.       Read(InIndex, wordcode);
  441.       If wordcode = 'i'
  442.         then begin kind := index;  ref := nil  end
  443.       Else if wordcode = 'c'
  444.         then begin kind := count;  ct  := 0    end
  445.       Else
  446.         Writeln('Erroneous word code in file InIndex.')
  447.     End;                        {with statement setting up node}
  448.     readln(InIndex)             {Advance to the start of the next entry.}
  449.   End
  450. End;                            {procedure GetNode}
  451.  
  452.  
  453.  
  454. Procedure Process( r: reference);
  455. {takes the word and page reference r and updates the binary tree}
  456. Var
  457.   p:          pointer;          {Trace through the tree.}
  458.   found:      Boolean;          {Is the word in the tree?}
  459. Begin                           {procedure Process}
  460.   If root = nil then            {The tree might be empty.}
  461.     NewWord(root, r)
  462.   Else begin                    {case of nonempty tree}
  463.     p := root;                  {Begin a tree search.}
  464.     found := false;
  465.     Repeat
  466.       If r.wd = p^.wd then
  467.         found := true
  468.       Else If Lt(r.wd, p^.wd) then
  469.         p := p^.left
  470.       Else
  471.         p := p^.right
  472.     Until found or (p = nil);
  473.     If found then UpdateNode(p, r)
  474.     Else begin                  {p^ was not found:  add it to the tree.}
  475.       NewWord(p, r);
  476.       InsertTree(root, p)
  477.     End
  478.   End
  479. End;                            {procedure Process}
  480.  
  481.  
  482.  
  483. Procedure UpdateNode( p:  pointer;  r: reference);
  484. {uses reference r to update information in node p^}
  485. Var
  486.   q:     pointref;              {used to add reference to list}
  487. Begin                {procedure UpdateNode}
  488.   With p^ do
  489.     Case  kind  of
  490.       hash:;                    {no action needed}
  491.       count: ct := ct + 1;
  492.       index: If ref = nil then
  493.              Begin
  494.                new(ref);
  495.                ref^.pg   := r.pg;
  496.                ref^.next := nil
  497.              End
  498.              Else if ref^.pg <> r.pg then
  499.              Begin            {Add the new reference to the list.}
  500.                New(q);
  501.                q^.pg   := r.pg;
  502.                q^.next := ref;
  503.                ref     := q
  504.              End
  505.     End                         {case statement to update tree}
  506. End;                            {procedure UpdateNode}
  507.  
  508.  
  509.  
  510. Procedure NewWord(var p: pointer;  r: reference);
  511. {Creates a node for the first occurrence of a new reference r.
  512.  A pointer to the new node is returned in p.}
  513. Var
  514.   response:       char;         {answer received from user}
  515. Begin                           {procedure NewWord}
  516.   new(p);
  517.   With p^ do
  518.   Begin
  519.     wd    := r.wd;
  520.     left  := nil;
  521.     right := nil;
  522.     Repeat                      {Ask user what kind of word.}
  523.       WriteWord(output, wd);
  524.       write('is (F, C, I)?');
  525.       read(response)
  526.     Until response in ['F', 'C', 'I' ,'f', 'c', 'i'];
  527.     Case response of
  528.        'F','f': kind := hash;
  529.        'C','c': Begin
  530.                   kind := count;
  531.                   ct   := 1
  532.                 End;
  533.        'I','i': Begin
  534.                   kind := index;
  535.                   new(ref);
  536.                   ref^.pg   := r.pg;
  537.                   ref^.next := nil;
  538.                 End
  539.     End                         {case statement}
  540.   End                           {with statement}
  541. End;                            {procedure NewWord}
  542.  
  543.  
  544.  
  545. Procedure InsertTree(r, p: pointer);
  546. {adds a node p^ to the tree with root r^;  requires that r <> nil
  547.  and p^ not be in the tree; proceeds by recursion}
  548. Begin                           {procedure InsertTree}
  549.   If Lt(p^.wd, r^.wd) then
  550.     If r^.left = nil then r^.left := p
  551.     Else InsertTree(r^.left, p)
  552.   Else
  553.     If r^.right = nil then r^.right := p
  554.     Else InsertTree(r^.right, p)
  555. End;                            {procedure InsertTree}
  556.  
  557.  
  558.  
  559. Procedure OutputTree( p: pointer);
  560. {traverses the tree for which p^ is the root in inorder}
  561. Begin                           {procedure OutputTree}
  562.   If p <> nil then
  563.   With p^ do
  564.   Begin
  565.     OutputTree(left);           {Traverse the left subtree.}
  566.     PutNode(p);
  567.     OutputTree(right);          {Traverse the right subtree.}
  568.     Dispose(p)
  569.   End
  570. End;                            {procedure OutputTree}
  571.  
  572.  
  573.  
  574. Procedure PutNode(p:  pointer);
  575. Var
  576.   q:        pointref;           {used to traverse list of references}
  577. Begin                           {procedure PutNode}
  578.   With p^ do
  579.     Case  kind  of
  580.       hash: Begin
  581.               NewHashFile^ := wd;
  582.               put( NewHashFile )
  583.             End;
  584.       count: If ct <> 0 then   {Otherwise, word is not in the document.}
  585.              Begin
  586.                WriteWord(NewIndex, wd);
  587.                write(NewIndex, 'c');
  588.                writeln( NewIndex, ct:5)
  589.              End;
  590.       index: If ref <> nil then
  591.              Begin
  592.                WriteWord(NewIndex, wd);
  593.                write(NewIndex, 'i');
  594.                q := ref;
  595.                Repeat
  596.                  write( NewIndex, q^.pg:5);
  597.                  q := q^.next
  598.                Until q = nil;
  599.                writeln( NewIndex )
  600.              End
  601.     End                         {case statement}
  602. End;                            {procedure PutNode}
  603.  
  604.  
  605.  
  606.  
  607.  
  608. {11.5  Phase 3:  Updating the Permanent Files}
  609.  
  610.  
  611. Procedure UpdateHashFile;
  612. {reads in old hash table, inserts file of new entries; writes out to HashFile}
  613. Var
  614.   hash:   array[hashentry] of word;
  615.   x:      hashentry;
  616.   w:      word;
  617. Begin                           {procedure UpdateHashFile}
  618.   reset(HashFile);
  619.   If eof(HashFile) then         {HashFile is empty; create new table.}
  620.     For x := 1 to hashsize do
  621.       hash[x] := blankword
  622.   Else
  623.     For  x := 1 to hashsize do
  624.       read(HashFile, hash[x]);
  625. {Some versions of Pascal do not allow procedures read and write for
  626.      files other than text.  For such systems, expand to use get and put.}
  627.   reset(NewHashFile);
  628.   While not eof(NewHashFile) do
  629.   Begin
  630.     read(NewHashFile, w);
  631.     hash[HashAddress(w)] := w
  632.             {If the table is full, new entries will replace old ones.}
  633.   End;
  634.   rewrite(HashFile);
  635.   For x := 1 to hashsize do
  636.     Write(HashFile, hash[x])
  637. End;                            {procedure UpdateHashFile}
  638.  
  639.  
  640.  
  641. Procedure MergeIndices;
  642. {merges files NewIndex and InIndex into file OutIndex}
  643. Var
  644.   u, v:   word;        {for new and old indices, respectively}
  645.   m, n:   integer;    {counts for above entries}
  646.   ukind,
  647.   vkind:  char;                 {Is the word of kind i or c?}
  648. Begin                           {procedure MergeIndices}
  649.   reset(NewIndex);
  650.   reset(InIndex);
  651.   rewrite(OutIndex);
  652.   If eof(NewIndex) or eof(InIndex) then
  653.     writeln('One of the indices is empty.  No merge will be done.')
  654.   Else Begin
  655.     ReadWord(NewIndex, u);
  656.     ReadWord( InIndex, v);
  657.     Repeat
  658.       If Lt(u,v) then
  659.         CopyLine(u, NewIndex, true, true)
  660. {Boolean parameters mean, respectively;  start new line; end the line.}
  661.       Else If Lt(v,u) then
  662.         CopyLine(v,  InIndex, true, true)
  663.       Else begin                {Words are equal.  Determine the kind of word.}
  664.         read(NewIndex, ukind);
  665.         read( InIndex, vkind);
  666.         If ukind <> vkind then
  667.           writeln('Inconsistent word types found in merge.');
  668.         WriteWord(OutIndex, u);
  669.         write(OutIndex, ukind);
  670.         If ukind = 'c' then
  671.         Begin
  672.           readln(NewIndex, m);
  673.           readln( InIndex, n);
  674.           m := m + n;
  675.           writeln(OutIndex, m:5);
  676.           If not eof(NewIndex) then ReadWord(NewIndex, u);
  677.           If not eof( InIndex) then ReadWord( InIndex, v)
  678.         End
  679.         Else begin              {Copy both lists of page numbers.}
  680.           CopyLine(u, NewIndex, false, false);
  681.           CopyLine(v,  InIndex, false, true)
  682.         End
  683.       End        {finished processing equal words}
  684.     Until eof(NewIndex) or eof(InIndex);
  685.     While not eof(NewIndex) do
  686.       CopyLine(u, NewIndex, true, true);
  687.     While not eof(InIndex) do
  688.       CopyLine(v, InIndex, true, true)
  689. {At most one of the two loops above will iterate.}
  690.   End
  691. End;                            {procedure MergeIndices}
  692.  
  693.  
  694.  
  695. Procedure CopyLine( var w: word; var F: text; newline, endline:  Boolean);
  696. {Copies the remainder of a line from the file F to OutIndex.
  697.  If newline is true, then the word w is also written, and kind is copied.
  698.  If endline is true, then the line written to OutIndex is ended.
  699.  The procedure also reads a new word w from the next line in F.}
  700. Var
  701.   n:      integer;              {number copied from file to file}
  702.   kind:   char;                 {word code copied from file to file}
  703. Begin                           {procedure CopyLine}
  704.   If newline then
  705.   Begin
  706.     WriteWord(OutIndex, w);
  707.     read(F, kind);
  708.     write(OutIndex, kind)
  709.   End Else
  710.     While (not eof(F)) and (not eoln(F)) and (F^ = blank) do
  711.       get(F);
  712.   While (not eof(F)) and (not eoln(F)) do
  713.   Begin
  714.     read(F, n);
  715.     write(OutIndex,  n:5);
  716.     While (not eoln(F)) and (F^ = blank) do
  717.       get(F);                   {Skip blanks.}
  718.   End;
  719.   readln(F);
  720.   If not eof(F) then
  721.     ReadWord(F, w);
  722.   If endline then writeln(OutIndex)
  723. End;                            {procedure CopyLine}
  724.  
  725.  
  726. {
  727.  1. Program IndexText(InText, InIndex, NewIndex, OutIndex,
  728.                               HashFile, Input, Output);
  729.  2.    Function Lt(u, v: word):  Boolean;
  730.  3.    Procedure ReadWord(var F: text;  var w: word);
  731.  4.    Procedure WriteWord(var F: text; w: word);
  732.  
  733.  5.    Procedure SplitWords;                                     phase 1
  734.  6.      Function HashAddress(w: word):  hashentry;
  735.  7.      Procedure Initialize;
  736.  8.      Procedure GetWord;
  737.  9.        Procedure GetChar(var ch: char);
  738. 10.        Procedure AddChar(ch: char);
  739. 11.      Procedure Conclude;
  740.  
  741. 12.    Procedure ClassifyWords;                                  phase 2
  742. 13.      Procedure BuildTree(var root: pointer; ch: char);
  743. 14.        Procedure Insert(p: pointer);
  744. 15.        Function Power2(c: integer): level;
  745. 16.        Procedure FindRoot;
  746. 17.        Procedure ConnectSubtrees;
  747. 18.        Procedure GetNode(var p: pointer; ch: char);
  748. 19.      Procedure Process(r: reference);
  749. 20.        Procedure UpdateNode(p: pointer; r: reference);
  750. 21.        Procedure NewWord(var p: pointer; r: reference);
  751. 22.        Procedure InsertTree(r, p: pointer);
  752. 23.      Procedure OutputTree(p: pointer);
  753. 24.        Procedure PutNode(p: pointer);
  754.  
  755. 25.    Procedure UpdateHashFile;                                 phase 3
  756. 26.      Function HashAddress(w: word): hashentry;
  757. 27.    Procedure MergeIndices;
  758. 28.      Procedure CopyLine
  759. }
  760.  
  761.